/*
---
name: Picker.Date
description: Creates a DatePicker, can be used for picking years/months/days and time, or all of them
authors: Arian Stolwijk
requires: [Picker, Picker.Attach, Locale.en-US.DatePicker, More/Locale, More/Date]
provides: Picker.Date
...
*/

(function() {

	this.DatePicker = Picker.Date = new Class(
			{

				Extends : Picker.Attach,

				options : {/*
							 * onSelect: function(date){},
							 * 
							 * minDate: new Date('3/4/2010'), // Date object or
							 * a string maxDate: new Date('3/4/2011'), // same
							 * as minDate availableDates: {}, //
							 * invertAvailable: false,
							 * 
							 * format: null,
							 */

					// EVL
					onClose : function() {
						if (this.options.input_global != null) {
							this.options.input_global.removeEvents('keydown');
						}
						this.options.selection = new Array();
					},
					// end EVL

					timePicker : false,
					timePickerOnly : false, // deprecated, use onlyView = 'time'
					timeWheelStep : 1, // 10,15,20,30

					yearPicker : true,
					yearsPerPage : 20,

					startDay : 1, // Sunday (0) through Saturday (6) - be
					// aware that this may affect your layout,
					// since the days on the right might have a
					// different margin

					startView : 'days', // allowed values: {time, days, months,
					// years}
					openLastView : false,
					pickOnly : false, // 'years', 'months', 'days', 'time'
					canAlwaysGoUp : [ 'months', 'days' ],
					updateAll : false, // whether or not to update all inputs
					// when selecting a date

					weeknumbers : false,

					// if you like to use your own translations
					months_abbr : null,
					days_abbr : null,
					years_title : function(date, options) {
						var year = date.get('year');
						return year + '-' + (year + options.yearsPerPage - 1);
					},
					months_title : function(date, options) {
						return date.get('year');
					},
					days_title : function(date, options) {
						return date.format('%b %Y');
					},
					time_title : function(date, options) {
						return (options.pickOnly == 'time') ? Locale
								.get('DatePicker.select_a_time') : date
								.format('%d %B, %Y');
					}

					// EVL
					,
					input_global : null,

					date_global : null,

					control : false
				// end EVL
				},

				initialize : function(attachTo, options) {
					this.parent(attachTo, options);

					this.setOptions(options);
					options = this.options;

					// If we only want to use one picker / backwards
					// compatibility
					[ 'year', 'month', 'day', 'time' ].some(function(what) {
						if (options[what + 'PickerOnly']) {
							options.pickOnly = what;
							return true;
						}
						return false;
					});
					if (options.pickOnly) {
						options[options.pickOnly + 'Picker'] = true;
						options.startView = options.pickOnly;
					}

					// backward compatibility for startView
					var newViews = [ 'days', 'months', 'years' ];
					[ 'month', 'year', 'decades' ].some(function(what, i) {
						return (options.startView == what)
								&& (options.startView = newViews[i]);
					});

					options.canAlwaysGoUp = options.canAlwaysGoUp ? Array
							.from(options.canAlwaysGoUp) : [];

					// Set the min and max dates as Date objects
					if (options.minDate) {
						if (!(options.minDate instanceof Date))
							options.minDate = Date.parse(options.minDate);
						options.minDate.clearTime();
					}
					if (options.maxDate) {
						if (!(options.maxDate instanceof Date))
							options.maxDate = Date.parse(options.maxDate);
						options.maxDate.clearTime();
					}

					if (!options.format) {
						options.format = (options.pickOnly != 'time') ? Locale
								.get('Date.shortDate') : '';
						if (options.timePicker)
							options.format = (options.format)
									+ (options.format ? ' ' : '')
									+ Locale.get('Date.shortTime');
					}

					// Some link or input has fired an event!
					this.addEvent('attached', function(event, element) {
						var self = this;
						// This is where we store the selected date
						if (!this.currentView || !options.openLastView)
							this.currentView = options.startView;

						this.date = limitDate(new Date(), options.minDate,
								options.maxDate);
						var tag = element.get('tag'), input;
						if (tag == 'input')
							input = element;
						else {
							var index = this.toggles.indexOf(element);
							if (this.inputs[index])
								input = this.inputs[index];
						}
						this.getInputDate(input, options);
						this.input = input;

						// EVL
						this.options.input_global = input;
						this.options.date_global = this.date;
						this.options.input_global.addEvent('keydown', function(
								event) {
							self.keydownEvent(event);
						});

						this.options.input_global = input;
						// end EVL

						this.setColumns(this.originalColumns);
					}.bind(this), true);

					// EVL
					var self = this;
					var picker = document.id(options.pickerClass + ' column_'
							+ options.columns);
					picker.addEvent('keydown', function(event) {
						self.keydownEvent(event);
					});

					picker.setProperty('tabindex', 0);
					picker.focus();
					// end EVL

				},

				keydownEvent : function(event) {

					var keyCode;
					// works in IE 8 and FF 3
					if (window.event) {
						keyCode = window.event.keyCode;
					} else {
						keyCode = event.code;
					}

					switch (keyCode) {
					case 13: // select date (according to WAI ARIA Authoring
						// Practices 1.0)
					case 32:
						event.stop();
						this.select(this.options.date_global);
						break;

					case 37: // left arrow --> go to previous day (according
						// to WAI ARIA Authoring Practices 1.0)
						event.stop();
						this.options.date_global = this.options.date_global
								.clone().decrement('day', 1);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 39: // right arrow --> go to next day (according to
						// WAI ARIA Authoring Practices 1.0)
						event.stop();
						this.options.date_global = this.options.date_global
								.clone().increment('day', 1);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 38:
						event.stop();
						// up arrow + ctrl --> go to same date in previous year
						// (instead of page up + ctrl because of firefox
						// keystrokes)
						if (event.control) {
							this.options.date_global = this.options.date_global
									.clone().decrement('year', 1);
							this.renderDays(this.options.date_global, 'fade');
							this.options.control = false;
						} // up arrow --> go to same day in previous week
						// (according to WAI ARIA Authoring Practices 1.0)
						else {
							this.options.date_global = this.options.date_global
									.clone().decrement('week', 1);
							this.renderDays(this.options.date_global, 'fade');
						}
						break;

					case 40:
						event.stop();
						// down arrow + ctrl --> go to same date in next year
						// (instead of page down + ctrl because of firefox
						// keystrokes)
						if (event.control) {
							this.options.date_global = this.options.date_global
									.clone().increment('year', 1);
							this.renderDays(this.options.date_global, 'fade');
							this.options.control = false;
						}
						// down arrow --> go to same day in next week (according
						// to WAI ARIA Authoring Practices 1.0)
						else {
							this.options.date_global = this.options.date_global
									.clone().increment('week', 1);
							this.renderDays(this.options.date_global, 'fade');
						}
						break;

					case 33: // page up --> go to same date in previous month
						// (according to WAI ARIA Authoring Practices
						// 1.0)
						event.stop();
						this.options.date_global = this.options.date_global
								.clone().decrement('month', 1);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 34: // page down --> go to same date in next month
						// (according to WAI ARIA Authoring Practices
						// 1.0)
						event.stop();
						this.options.date_global = this.options.date_global
								.clone().increment('month', 1);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 36: // Home --> go to first day of current month
						// (according to WAI ARIA Authoring Practices
						// 1.0)
						event.stop();
						this.options.date_global = this.options.date_global
								.clone().set('date', 1);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 35: // End --> go to last day of current month
						// (according to WAI ARIA Authoring Practices
						// 1.0)
						event.stop();
						this.day_number = this.options.date_global.clone().get(
								'lastdayofmonth');
						this.options.date_global = this.options.date_global
								.clone().set('date', this.day_number);
						this.renderDays(this.options.date_global, 'fade');
						break;

					case 27: // Escape --> close date picker without any
						// action (according to WAI ARIA Authoring
						// Practices 1.0)
						event.stop();
						this.close();
						break;

					case 9: // Tab --> close date picker without any action and
						// move the focus (according to WAI ARIA Authoring
						// Practices 1.0)
						this.close();
						break;
					}
				},

				getInputDate : function(input, options) {
					this.date = new Date();
					if (!input)
						return;
					var date = Date.parse(input.get('value'));
					if (date == null || !date.isValid()) {
						var storeDate = input.retrieve('datepicker:value');
						if (storeDate)
							date = Date.parse(storeDate);
					}
					if (date != null && date.isValid())
						this.date = date;

					// EVL
					this.options.date_global = date;
					// end EVL
				},

				// Control the previous and next elements

				constructPicker : function() {
					this.parent();

					// AD
					// changed arrows to spaces
					this.previous = new Element('div.previous[html=&#160;]')
							.inject(this.header).setProperty('role', 'button')
							.setProperty('aria-label',
									'Go back to the previous month');
					this.next = new Element('div.next[html=&#160;]').inject(
							this.header).setProperty('role', 'button')
							.setProperty('aria-label',
									'Advance to the next month');
					// end AD
				},

				hidePrevious : function(_next, _show) {
					this[_next ? 'next' : 'previous'].setStyle('display',
							_show ? 'block' : 'none');
					return this;
				},

				showPrevious : function(_next) {
					return this.hidePrevious(_next, true);
				},

				setPreviousEvent : function(fn, _next) {
					this[_next ? 'next' : 'previous'].removeEvents('click');
					if (fn)
						this[_next ? 'next' : 'previous'].addEvent('click', fn);
					return this;
				},

				hideNext : function() {
					return this.hidePrevious(true);
				},

				showNext : function() {
					return this.showPrevious(true);
				},

				setNextEvent : function(fn) {
					return this.setPreviousEvent(fn, true);
				},

				setColumns : function(columns, view, date, viewFx) {
					var ret = this.parent(columns), method;

					if ((view || this.currentView)
							&& (method = 'render'
									+ (view || this.currentView).capitalize())
							&& this[method])
						this[method](date || this.date.clone(), viewFx);

					return ret;
				},

				// Render the Pickers

				renderYears : function(date, fx) {

					var options = this.options, pages = options.columns, perPage = options.yearsPerPage, _columns = [], _dates = [];
					this.dateElements = [];

					// start neatly at interval (eg. 1980 instead of 1987)
					date = date.clone().decrement('year',
							date.get('year') % perPage);

					var iterateDate = date.clone().decrement('year',
							Math.floor((pages - 1) / 2) * perPage);

					for ( var i = pages; i--;) {
						var _date = iterateDate.clone();
						_dates.push(_date);
						_columns.push(renderers.years(timesSelectors.years(
								options, _date.clone()), options, this.date
								.clone(), this.dateElements, function(date) {
							if (options.pickOnly == 'years')
								this.select(date);
							else
								this.renderMonths(date, 'fade');
							this.date = date;
						}.bind(this)));
						iterateDate.increment('year', perPage);
					}

					this.setColumnsContent(_columns, fx);
					this.setTitle(_dates, options.years_title);

					// Set limits
					var limitLeft = (options.minDate && date.get('year') <= options.minDate
							.get('year')), limitRight = (options.maxDate && (date
							.get('year') + options.yearsPerPage) >= options.maxDate
							.get('year'));
					this[(limitLeft ? 'hide' : 'show') + 'Previous']();
					this[(limitRight ? 'hide' : 'show') + 'Next']();

					this.setPreviousEvent(function() {
						this.renderYears(date.decrement('year', perPage),
								'left');
					}.bind(this));

					this.setNextEvent(function() {
						this.renderYears(date.increment('year', perPage),
								'right');
					}.bind(this));

					// We can't go up!
					this.setTitleEvent(null);

					this.currentView = 'years';
				},

				renderMonths : function(date, fx) {

					var options = this.options, years = options.columns, _columns = [], _dates = [], iterateDate = date
							.clone().decrement('year',
									Math.floor((years - 1) / 2));
					this.dateElements = [];

					for ( var i = years; i--;) {
						var _date = iterateDate.clone();
						_dates.push(_date);
						_columns.push(renderers.months(timesSelectors.months(
								options, _date.clone()), options, this.date
								.clone(), this.dateElements, function(date) {
							if (options.pickOnly == 'months')
								this.select(date);
							else
								this.renderDays(date, 'fade');

							this.date = date;
						}.bind(this)));
						iterateDate.increment('year', 1);
					}

					this.setColumnsContent(_columns, fx);
					this.setTitle(_dates, options.months_title);

					// Set limits
					var year = date.get('year'), limitLeft = (options.minDate && year <= options.minDate
							.get('year')), limitRight = (options.maxDate && year >= options.maxDate
							.get('year'));
					this[(limitLeft ? 'hide' : 'show') + 'Previous']();
					this[(limitRight ? 'hide' : 'show') + 'Next']();

					this.setPreviousEvent(function() {
						this
								.renderMonths(date.decrement('year', years),
										'left');
					}.bind(this));

					this.setNextEvent(function() {
						this.renderMonths(date.increment('year', years),
								'right');
					}.bind(this));

					var canGoUp = options.yearPicker
							&& (options.pickOnly != 'months' || options.canAlwaysGoUp
									.contains('months'));
					var titleEvent = (canGoUp) ? function() {
						this.renderYears(date, 'fade');
					}.bind(this) : null;
					this.setTitleEvent(titleEvent);

					this.currentView = 'months';
				},

				renderDays : function(date, fx) {
					var options = this.options, months = options.columns, _columns = [], _dates = [], iterateDate = date
							.clone().decrement('month',
									Math.floor((months - 1) / 2));
					this.dateElements = [];

					for ( var i = months; i--;) {
						_date = iterateDate.clone();
						_dates.push(_date);
						_columns.push(renderers.days(

						timesSelectors.days(options, _date.clone()), options,
						// this.date.clone(),
						// EVL
						this.options.date_global.clone(),
						// end EVL
						this.dateElements, function(date) {
							if (options.pickOnly == 'days'
									|| !options.timePicker)
								this.select(date);
							else

								this.renderTime(date, 'fade');

							this.date = date;
						}.bind(this)));

						iterateDate.increment('month', 1);
					}

					this.setColumnsContent(_columns, fx);
					this.setTitle(_dates, options.days_title);

					var yearmonth = date.format('%Y%m').toInt(), limitLeft = (options.minDate && yearmonth <= options.minDate
							.format('%Y%m')), limitRight = (options.maxDate && yearmonth >= options.maxDate
							.format('%Y%m'));
					this[(limitLeft ? 'hide' : 'show') + 'Previous']();
					this[(limitRight ? 'hide' : 'show') + 'Next']();

					this.setPreviousEvent(function() {
						this
								.renderDays(date.decrement('month', months),
										'fade');
					}.bind(this));

					this.setNextEvent(function() {
						this
								.renderDays(date.increment('month', months),
										'fade');
					}.bind(this));

					var canGoUp = options.pickOnly != 'days'
							|| options.canAlwaysGoUp.contains('days');
					var titleEvent = (canGoUp) ? function() {
						this.renderMonths(date, 'fade');
					}.bind(this) : null;
					this.setTitleEvent(titleEvent);

					this.currentView = 'days';
				},

				renderTime : function(date, fx) {

					var options = this.options;
					this.setTitle(date, options.time_title);

					var originalColumns = this.originalColumns = options.columns;
					this.currentView = null; // otherwise you'd get crazy
					// recursion
					if (originalColumns != 1)
						this.setColumns(1);

					this.setContent(renderers.time(options, date.clone(),
							function(date) {
								this.select(date);
							}.bind(this)), fx);

					// Hide buttons
					this.hidePrevious().hideNext().setPreviousEvent(null)
							.setNextEvent(null);

					var canGoUp = options.pickOnly != 'time'
							|| options.canAlwaysGoUp.contains('time');
					var titleEvent = (canGoUp) ? function() {
						this.setColumns(originalColumns, 'days', date, 'fade');
					}.bind(this) : null;
					this.setTitleEvent(titleEvent);

					this.currentView = 'time';
				},

				select : function(date, all) {

					this.date = date;
					var formatted = date.format(this.options.format), time = date
							.strftime(), inputs = (!this.options.updateAll
							&& !all && this.input) ? [ this.input ]
							: this.inputs;

					inputs.each(function(input) {
						input.set('value', formatted).store('datepicker:value',
								time).fireEvent('change');
					}, this);

					this.fireEvent('select', [ date ].concat(inputs));
					this.close();
					return this;
				}

			});

	// Renderers only output elements and calculate the limits!

	var timesSelectors = {

		years : function(options, date) {
			var times = [];
			for ( var i = 0; i < options.yearsPerPage; i++) {
				times.push(+date);
				date.increment('year', 1);
			}
			return times;
		},

		months : function(options, date) {
			var times = [];
			date.set('month', 0);
			for ( var i = 0; i <= 11; i++) {
				times.push(+date);
				date.increment('month', 1);
			}
			return times;
		},

		days : function(options, date) {
			var times = [];
			date.set('date', 1);
			while (date.get('day') != options.startDay)
				date.set('date', date.get('date') - 1);
			for ( var i = 0; i < 42; i++) {
				times.push(+date);
				date.increment('day', 1);
			}
			return times;
		}

	};

	var renderers = {

		// Jahreszahlen werden angezeigt, nachdem man auf das Jahr
		// doppelgeklickt hat.
		years : function(years, options, currentDate, dateElements, fn) {
			var container = new Element('div.years'), today = new Date(), element, classes;

			years.each(function(_year, i) {
				var date = new Date(_year), year = date.get('year');

				classes = '.year.year' + i;
				if (year == today.get('year'))
					classes += '.today';
				if (year == currentDate.get('year'))
					classes += '.selected';
				element = new Element('div' + classes, {
					text : year
				}).inject(container);

				dateElements.push({
					element : element,
					time : _year
				});

				if (isUnavailable('year', date, options))
					element.addClass('unavailable');
				else {
					element.addEvent('click', fn.pass(date));
				}
			});

			return container;
		},

		months : function(months, options, currentDate, dateElements, fn) {
			var today = new Date(), month = today.get('month'), thisyear = today
					.get('year'), selectedyear = currentDate.get('year'), container = new Element(
					'div.months'), monthsAbbr = options.months_abbr
					|| Locale.get('Date.months_abbr'), element, classes;

			months.each(function(_month, i) {
				var date = new Date(_month), year = date.get('year');

				classes = '.month.month' + (i + 1);
				if (i == month && year == thisyear)
					classes += '.today';
				if (i == currentDate.get('month') && year == selectedyear)
					classes += '.selected';
				element = new Element('div' + classes, {
					text : monthsAbbr[i]
				}).inject(container);

				dateElements.push({
					element : element,
					time : _month
				});

				if (isUnavailable('month', date, options))
					element.addClass('unavailable');
				else
					element.addEvent('click', fn.pass(date));
			});

			return container;
		},

		days : function(days, options, currentDate, dateElements, fn) {
			var month = new Date(days[14]).get('month'), todayString = new Date()
					.toDateString(), currentString = currentDate.toDateString(), weeknumbers = options.weeknumbers, container = new Element(
					'table.days' + (weeknumbers ? '.weeknumbers' : ''), {
						role : 'grid',
						'aria-labelledby' : this.titleID
					}), header = new Element('thead').inject(container), body = new Element(
					'tbody').inject(container),

			titles = new Element('tr.titles').inject(header), localeDaysShort = options.days_abbr
					|| Locale.get('Date.days_abbr'), day, classes, element, weekcontainer, dateString;

			if (weeknumbers)
				new Element('th.title.day.weeknumber', {
					text : Locale.get('DatePicker.week')
				}).inject(titles);
			for (day = options.startDay; day < (options.startDay + 7); day++) {
				new Element('th.title.day.day' + (day % 7), {
					text : localeDaysShort[(day % 7)],
					role : 'columnheader'
				}).inject(titles);
			}

			days.each(function(_date, i) {

				var date = new Date(_date);

				if (i % 7 == 0) {
					weekcontainer = new Element('tr.week.week'
							+ (Math.floor(i / 7))).set('role', 'row').inject(
							body);
					if (weeknumbers)
						new Element('td.day.weeknumber', {
							text : date.get('week')
						}).inject(weekcontainer);
				}

				dateString = date.toDateString();
				classes = '.day.day' + date.get('day');
				if (dateString == todayString)
					classes += '.today';
				if (date.get('month') != month)
					classes += '.otherMonth';
				element = new Element('td' + classes, {
					text : date.getDate(),
					role : 'gridcell'
				}).inject(weekcontainer);

				// AD
				if (dateString == currentString) {
					element.addClass('selected').set('aria-selected', 'true');
					element.setProperty('tabIndex', 0);

					var tempEl = element;

					window.setTimeout(function() {
						tempEl.focus();
					}.bind(this), 100);

				} else {
					element.set('aria-selected', 'false');
				}
				// end AD

				dateElements.push({
					element : element,
					time : _date
				});

				if (isUnavailable('date', date, options))
					element.addClass('unavailable');
				else {
					element.addEvent('click', fn.pass(date.clone()));
				}

			});

			return container;
		},

		time : function(options, date, fn) {
			var container = new Element('div.time'),
			// make sure that the minutes are timeWheelStep * k
			initMinutes = (date.get('minutes') / options.timeWheelStep).round()
					* options.timeWheelStep;

			if (initMinutes >= 60)
				initMinutes = 0;
			date.set('minutes', initMinutes);

			var hoursInput = new Element('input.hour[type=text]', {
				title : Locale.get('DatePicker.use_mouse_wheel'),
				value : date.format('%H'),
				events : {
					click : function(event) {
						event.target.focus();
						event.stop();
					},
					mousewheel : function(event) {
						event.stop();
						hoursInput.focus();
						var value = hoursInput.get('value').toInt();
						value = (event.wheel > 0) ? ((value < 23) ? value + 1
								: 0) : ((value > 0) ? value - 1 : 23)
						date.set('hours', value);
						hoursInput.set('value', date.format('%H'));
					}.bind(this)
				},
				maxlength : 2
			}).inject(container);

			var minutesInput = new Element(
					'input.minutes[type=text]',
					{
						title : Locale.get('DatePicker.use_mouse_wheel'),
						value : date.format('%M'),
						events : {
							click : function(event) {
								event.target.focus();
								event.stop();
							},
							mousewheel : function(event) {
								event.stop();
								minutesInput.focus();
								var value = minutesInput.get('value').toInt();
								value = (event.wheel > 0) ? ((value < 59) ? (value + options.timeWheelStep)
										: 0)
										: ((value > 0) ? (value - options.timeWheelStep)
												: (60 - options.timeWheelStep));
								if (value >= 60)
									value = 0;
								date.set('minutes', value);
								minutesInput.set('value', date.format('%M'));
							}.bind(this)
						},
						maxlength : 2
					}).inject(container);

			new Element('div.separator[text=:]').inject(container);

			new Element('input.ok[type=submit]', {
				value : Locale.get('DatePicker.time_confirm_button'),
				events : {
					click : function(event) {
						event.stop();
						date.set({
							hours : hoursInput.get('value').toInt(),
							minutes : minutesInput.get('value').toInt()
						});
						fn(date.clone());
					}
				}
			}).inject(container);

			return container;
		}

	};

	Picker.Date.defineRenderer = function(name, fn) {
		renderers[name] = fn;
		return this;
	};

	var limitDate = function(date, min, max) {
		if (min && date < min)
			return min;
		if (max && date > max)
			return max;
		return date;
	};

	var isUnavailable = function(type, date, options) {
		var minDate = options.minDate, maxDate = options.maxDate, availableDates = options.availableDates, year, month, day, ms;

		if (!minDate && !maxDate && !availableDates)
			return false;
		date.clearTime();

		if (type == 'year') {
			year = date.get('year');
			return ((minDate && year < minDate.get('year'))
					|| (maxDate && year > maxDate.get('year')) || ((availableDates != null && !options.invertAvailable) && (availableDates[year] == null
					|| Object.getLength(availableDates[year]) == 0 || Object
					.getLength(Object.filter(availableDates[year], function(
							days) {
						return (days.length > 0);
					})) == 0)));
		}

		if (type == 'month') {
			year = date.get('year');
			month = date.get('month') + 1;
			ms = date.format('%Y%m').toInt();
			return ((minDate && ms < minDate.format('%Y%m').toInt())
					|| (maxDate && ms > maxDate.format('%Y%m').toInt()) || ((availableDates != null && !options.invertAvailable) && (availableDates[year] == null
					|| availableDates[year][month] == null || availableDates[year][month].length == 0)));
		}

		// type == 'date'
		year = date.get('year');
		month = date.get('month') + 1;
		day = date.get('date');

		var dateAllow = (minDate && date < minDate)
				|| (minDate && date > maxDate);
		if (availableDates != null) {
			dateAllow = dateAllow || availableDates[year] == null
					|| availableDates[year][month] == null
					|| !availableDates[year][month].contains(day);
			if (options.invertAvailable)
				dateAllow = !dateAllow;
		}

		return dateAllow;
	};

})();
